package com.aisino.encrypt.algorithm;

import com.aisino.encrypt.exception.AisinoException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;


/**
 * 非对称加密算法RSA算法组件
 *
 * @author 蒋帆
 * @datetime 2021-08-27
 * @description 1.甲方在本地构建密钥对（公钥+私钥），并将公钥公布给乙方
 * 2.甲方将数据用私钥进行加密，发送给乙方
 * 3.乙方用甲方提供的公钥对数据进行解密
 */
public class RSA {
    /**
     * 非对称密钥算法
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 密钥长度，ESA算法的默认密钥长度是512
     */
    private static final int KEY_SIZE = 512;
    //公钥
    private static final String PUBLIC_KEY = "RSAPublicKey";
    //私钥
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * 初始化密钥对
     *
     * @return Map 甲方密钥对的Map集合（公钥 + 私钥）
     */
    public static Map<String, Object> initKey() {
        Map<String, Object> keyMap = null;
        try {
            //实例化密钥生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            //初始化密钥生成器
            keyPairGenerator.initialize(KEY_SIZE);
            //生成密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //甲方公钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            //甲方私钥
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            //将密钥存储在map中
            keyMap = new HashMap<String, Object>();
            keyMap.put(PUBLIC_KEY, publicKey);
            keyMap.put(PRIVATE_KEY, privateKey);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        }
        return keyMap;
    }

    /**
     * 初始化密钥对
     *
     * @param keySize 初始化密钥对生成器，密钥大小为96-1024位
     * @return Map 甲方密钥对的Map集合（公钥 + 私钥）
     */
    public static Map<String, Object> initKey(int keySize) {
        if (keySize < 512 || keySize > 1024) {
            throw new AisinoException("密钥大小需要在512-1024位之间");
        }
        Map<String, Object> keyMap = null;
        try {
            //实例化密钥生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            //初始化密钥生成器
            keyPairGenerator.initialize(keySize);
            //生成密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //甲方公钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            //甲方私钥
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            //将密钥存储在map中
            keyMap = new HashMap<String, Object>();
            keyMap.put(PUBLIC_KEY, publicKey);
            keyMap.put(PRIVATE_KEY, privateKey);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        }
        return keyMap;
    }


    /**
     * 私钥加密
     *
     * @param content 待加密数据
     * @param key     密钥
     * @return String 加密数据
     */
    public static String encryptByPrivateKey(String content, String key) {
        String ciphertext = null;
        try {
            //取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //生成私钥
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
            //数据加密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            ciphertext = Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes()));
        } catch (NoSuchPaddingException e) {
            throw new AisinoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new AisinoException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (InvalidKeySpecException e) {
            throw new AisinoException(e);
        } catch (BadPaddingException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return ciphertext;
    }

    /**
     * 公钥加密
     *
     * @param content 待加密数据
     * @param key     密钥
     * @return String 加密数据
     */
    public static String encryptByPublicKey(String content, String key) {
        String ciphertext = null;
        try {
            //实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //初始化公钥
            //密钥材料转换
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
            //产生公钥
            PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

            //数据加密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            ciphertext = Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes()));
        } catch (NoSuchPaddingException e) {
            throw new AisinoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new AisinoException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (InvalidKeySpecException e) {
            throw new AisinoException(e);
        } catch (BadPaddingException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return ciphertext;
    }

    /**
     * 私钥解密
     *
     * @param ciphertext 待解密数据
     * @param key        密钥
     * @return String 解密数据
     */
    public static String decryptByPrivateKey(String ciphertext, String key) {
        String content = null;
        try {
            //取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //生成私钥
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
            //数据解密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            content = new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)));
        } catch (NoSuchPaddingException e) {
            throw new AisinoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new AisinoException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (InvalidKeySpecException e) {
            throw new AisinoException(e);
        } catch (BadPaddingException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return content;
    }

    /**
     * 公钥解密
     *
     * @param ciphertext 待解密数据
     * @param key        密钥
     * @return String 解密数据
     */
    public static String decryptByPublicKey(String ciphertext, String key) {
        String content = null;
        try {
            //实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //初始化公钥
            //密钥材料转换
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
            //产生公钥
            PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
            //数据解密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            content = new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)));
        } catch (NoSuchPaddingException e) {
            throw new AisinoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new AisinoException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (InvalidKeySpecException e) {
            throw new AisinoException(e);
        } catch (BadPaddingException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return content;
    }

    /**
     * 取得私钥
     *
     * @param keyMap 密钥map
     * @return String 私钥
     */
    public static String getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64.getEncoder().encodeToString(key.getEncoded());
    }

    /**
     * 取得公钥
     *
     * @param keyMap 密钥map
     * @return String 公钥
     */
    public static String getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64.getEncoder().encodeToString(key.getEncoded());
    }

}
